package de.tu_darmstadt.informatik.tk.lernsystem.dao;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import org.openrdf.repository.RepositoryException;
import org.openrdf.result.Result;

import de.tu_darmstadt.informatik.tk.lernsystem.concepts.Annotation;
import de.tu_darmstadt.informatik.tk.lernsystem.concepts.Note;
import de.tu_darmstadt.informatik.tk.lernsystem.concepts.Rating;
import de.tu_darmstadt.informatik.tk.lernsystem.concepts.Reference;
import de.tu_darmstadt.informatik.tk.lernsystem.concepts.Section;
import de.tu_darmstadt.informatik.tk.lernsystem.concepts.ShareRequest;
import de.tu_darmstadt.informatik.tk.lernsystem.concepts.Tag;
import de.tu_darmstadt.informatik.tk.lernsystem.concepts.TagAnnotation;
import de.tu_darmstadt.informatik.tk.lernsystem.concepts.Thing;
import de.tu_darmstadt.informatik.tk.lernsystem.concepts.User;
import de.tu_darmstadt.informatik.tk.lernsystem.concepts.Website;
import de.tu_darmstadt.informatik.tk.lernsystem.concepts.WebsiteSection;
import de.tu_darmstadt.informatik.tk.lernsystem.util.Md5;

/**
 * DAO for managing references & co.
 * Most methods here have corresponding actions in ReferenceServlet.
 * Please take a look at this class for further information.
 */
public class UriDao extends RdfDao {
	public static Reference addReference(Section baseSection, Section targetSection, User user) {
		Reference ref;
		ref = getReference(user, baseSection, targetSection);
		if (ref != null) {
			return ref;
		}
		ref = getReference(user, baseSection, null);
		if (ref == null) {
			ref = createObject(Reference.class, getRandomId());
			ref.setPartOf(baseSection);
			ref.setOwner(user);
			ref.setSourceSection(baseSection);
			baseSection.getAnnotations().add(ref);
		}
		if (getReference(user, targetSection, null) != null) {
			Reference ref2 = getReference(user, targetSection, null);
			targetSection.getAnnotations().remove(ref2);
			try {
				getConnection().removeDesignation(ref2, Reference.class);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		ref.setTargetSection(targetSection);
		targetSection.getAnnotations().add(ref);
		return ref;
	}
	
	public static Tag getTag(String text) {
		try {
			String uri = "urn:tag:" + Md5.getHash(text);
			Tag t = getConnection().addDesignation(getConnection().getObject(Tag.class, uri), Tag.class);
			t.setTag(text);
			return t;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static List<Tag> getTags(Section section, User user) {
		List<Tag> result = new ArrayList<Tag>();
		List<Annotation> list = getAnnotations(section, user);
		for (Annotation a : list) {
			if (a instanceof TagAnnotation) {
				result.add(((TagAnnotation) a).getTag());
			}
		}
		return result;
	}
	
	public static Tag addTag(Section section, Tag tag, User user) {
		TagAnnotation a = createObject(TagAnnotation.class, getRandomId());
		a.setTag(tag);
		a.setPartOf(section);
		a.setOwner(user);
		if (section.getAnnotations() == null) {
			section.setAnnotations(new ArrayList<Annotation>());
		}
		section.getAnnotations().add(a);
		return tag;
	}
	
	public static Note addNote(Section section, String text, User user) {
		Note note = createObject(Note.class, getRandomId());
		note.setText(text);
		note.setOwner(user);
		note.setPartOf(section);
		if (section.getAnnotations() == null) {
			section.setAnnotations(new ArrayList<Annotation>());
		}
		section.getAnnotations().add(note);
		return note;
	}
	
	public static Annotation getAnnotation(String id) {
		try {
			return getConnection().addDesignation(getConnection().getObject(Annotation.class, id), Annotation.class);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static void deleteAnnotation(Annotation a) {
		if (a == null) {
			return;
		}
		try {
			if (a instanceof Note) {
				a.getPartOf().getAnnotations().remove((Note)a);
				getConnection().removeDesignation(a, Note.class);
			} else if (a instanceof Reference) {
				((Reference) a).getSourceSection().getAnnotations().remove((Reference)a);
				((Reference) a).getTargetSection().getAnnotations().remove((Reference)a);
				getConnection().removeDesignation(a, Reference.class);
			} else if (a instanceof TagAnnotation) {
				a.getPartOf().getAnnotations().remove((TagAnnotation)a);
				getConnection().removeDesignation(a, TagAnnotation.class);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void deleteWebsiteSection(WebsiteSection section) {
		Website website = (Website)section.getPartOf();
		website.getSections().remove(section);
		section.setPartOf(null);
		
		try {
			List<Annotation> annotations = section.getAnnotations();
			for (Annotation a : annotations) {
				if (a instanceof Reference) {
					Reference ref = (Reference)a;
					if (section.equals(ref.getSourceSection()) && ref.getTargetSection() != null) {
						ref.getTargetSection().getAnnotations().remove(ref);
					} else if (section.equals(ref.getTargetSection()) && ref.getSourceSection() != null) {
						ref.getSourceSection().getAnnotations().remove(ref);
					}
					getConnection().removeDesignation(ref, Reference.class);
				} else {
					getConnection().removeDesignation(a, Annotation.class);
				}
			}
		
			getConnection().removeDesignation(section, WebsiteSection.class);
			if (website.getSections().isEmpty()) {
				getConnection().removeDesignation(website, Website.class);
			}
		} catch (RepositoryException e) {
			e.printStackTrace();
		}
	}
	
	public static Reference getReference(User user, Section baseSection, Section targetSection) {
		List<Reference> list = getReferences(user);
		for (Reference ref : list) {
			if (ref.getOwner().equals(user)) {
				if ((baseSection == null && ref.getSourceSection() == null) || (ref.getSourceSection() != null && ref.getSourceSection().equals(baseSection))) {
					if ((targetSection == null && ref.getTargetSection() == null) || (ref.getTargetSection() != null && ref.getTargetSection().equals(targetSection))) {
						return ref;
					}
				}
			}
		}
		return null;
	}
	
	public static Reference queueReference(Section baseSection, User user) {
		if (getReference(user, baseSection, null) == null) {
			Reference ref = createObject(Reference.class, getRandomId());
			ref.setPartOf(baseSection);
			ref.setOwner(user);
			ref.setSourceSection(baseSection);
			ref.setTargetSection(null);
			baseSection.getAnnotations().add(ref);
			return ref;
		}
		return null;
	}
	
	public static List<Reference> getReferences(User user) {
		List<Annotation> annotations = getAnnotations(user);
		List<Reference> result = new ArrayList<Reference>();
		for (Annotation a : annotations) {
			if (a instanceof Reference) {
				result.add((Reference)a);
			}
		}
		return result;
	}
	
	public static List<Reference> getQueuedReferences(User user) {
		List<Annotation> annotations = getAnnotations(user);
		List<Reference> result = new ArrayList<Reference>();
		for (Annotation a : annotations) {
			if (a instanceof Reference) {
				if (((Reference) a).getTargetSection() == null) {
					result.add((Reference)a);
				}
			}
		}
		return result;
	}
	
	public static List<Reference> getReferences(Thing thing, User user) {
		if (thing == null) {
			return new ArrayList<Reference>();
		}
		List<Annotation> annotations = getAnnotations(thing, user);//TODO
		List<Reference> result = new ArrayList<Reference>();
		for (Annotation a : annotations) {
			if (a instanceof Reference) {
				if (((Reference) a).getTargetSection() != null) {
					result.add((Reference)a);
				}
			}
		}
		return result;
	}
	
	public static void answerShareRequest(String shareId, boolean accept) {
		try {
			ShareRequest sr = getConnection().addDesignation(getConnection().getObject(ShareRequest.class, shareId), ShareRequest.class);
			if (accept) {
				sr.setAccepted(true);
			} else {
				sr.getAnnotation().getShareRequests().remove(sr);
				getConnection().removeDesignation(sr, ShareRequest.class);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void addShareRequest(Annotation a, User forUser) {
		ShareRequest sr = createObject(ShareRequest.class, getRandomId());
		sr.setTargetUser(forUser);
		sr.setAnnotation(a);
		sr.setAccepted(false);
		if (a.getShareRequests() == null) {
			a.setShareRequests(new ArrayList<ShareRequest>()); 
		}
		a.getShareRequests().add(sr);
	}
	
	public static List<ShareRequest> getPendingShareRequests(User user) {
		List<ShareRequest> result = new ArrayList<ShareRequest>();
		try {
			Result<ShareRequest> srList = getConnection().getObjects(ShareRequest.class);
			for (ShareRequest sr : srList.asList()) {
				if (user.equals(sr.getTargetUser()) && !sr.getAccepted()) {
					result.add(sr);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	public static void setRating(Thing thing, User user, Integer rating) {
		Rating r = null;
		String uri = "urn:rating:"+Md5.getHash(thing.toString()) + ":" + Md5.getHash(user.toString());
		try {
			r = getConnection().addDesignation(getConnection().getObject(Rating.class, uri), Rating.class);
		} catch (Exception e) {
			e.printStackTrace();
		}
		r.setOwner(user);
		if (thing.getRatings() == null) {
			thing.setRatings(new HashSet<Rating>());
		}
		thing.getRatings().add(r);
		r.setRating(rating);
	}
	
	public static void setSemanticRelation(Reference ref, String semanticRelation) {
		ref.setSemanticRelation(semanticRelation);		
	}
	
	public static List<Annotation> getAnnotations(User user) {
		List<Annotation> annotations = new ArrayList<Annotation>();
		try {
			Result<Annotation> result = getConnection().getObjects(Annotation.class);
			for (Annotation a : result.asList()) {
				if (a.getOwner().equals(user)) {
					annotations.add(a);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return annotations;
	}
	
	public static List<Annotation> getAnnotations(Section section, User user) {
		List<Annotation> annotations = new ArrayList<Annotation>();
		for (Annotation a : section.getAnnotations()) {
			if (a.getOwner().equals(user)) {
				annotations.add(a);
			}
		}
		return annotations;
	}
	
	public static List<Annotation> getAnnotations(Thing thing, User user) {
		List<Annotation> annotations = new ArrayList<Annotation>();
		if (thing instanceof Website) {
			for(Section s : ((Website) thing).getSections()) {
				for (Annotation a : s.getAnnotations()) {
					if (user.equals(a.getOwner())) {
						annotations.add(a);
					} else if (a.getShareRequests() != null) {
						for (ShareRequest sr : a.getShareRequests()) {
							if (user.equals(sr.getTargetUser()) && sr.getAccepted()) {
								annotations.add(a);
							}
						}
					}
				}
			}
		}
		return annotations;
	}
	
	public static Thing getThing(String uri) {
		try {
			return getConnection().addDesignation(getConnection().getObject(Thing.class, uri), Thing.class);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static Reference getReferenceById(String refId) {
		try {
			return getConnection().addDesignation(getConnection().getObject(Reference.class, refId), Reference.class);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static Website factorWebsite(String uri) {
		Website website = null;
		try {
			website = getConnection().addDesignation(getConnection().getObject(Website.class, uri), Website.class);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (website == null) {
			website = createObject(Website.class, uri);
		}
		if (website.getSections() == null) {
			website.setSections(new ArrayList<WebsiteSection>());
		}
		return website;
	}
	
	public static WebsiteSection factorWebsiteSection(String uri, String xpath) {
		Website website = factorWebsite(uri);
		if (xpath == null) {
			xpath = "";
		}
		for (WebsiteSection section : website.getSections()) {
			String sectionXpath = section.getXpath();
			if (sectionXpath == null) {
				sectionXpath = "";
			}
			if (xpath.toLowerCase().equals(sectionXpath.toLowerCase())) {
				if (section.getAnnotations() == null) {
					section.setAnnotations(new ArrayList<Annotation>());
				}
				return section;
			}
		}
		WebsiteSection section = createObject(WebsiteSection.class, uri + "/__xpath__/" + xpath);
		section.setPartOf(website);
		section.setXpath(xpath);
		website.getSections().add(section);
		section.setAnnotations(new ArrayList<Annotation>());
		return section;
	}

}
